Skip to content

Conversation

@alexey-hunter-io
Copy link

@alexey-hunter-io alexey-hunter-io commented Dec 9, 2025

What this does

Fixes multi-turn conversation error when using with_schema for structured output. The persisted Hash/Array response was being passed directly to OpenAI instead of being serialized as a JSON string.

OpenAI API expects message content to be a string, so structured output Hashes/Arrays stored in content_raw must be converted to JSON when replayed.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Performance improvement

Scope check

  • I read the Contributing Guide
  • This aligns with RubyLLM's focus on LLM communication
  • This isn't application-specific logic that belongs in user code
  • This benefits most users, not just my specific use case

Quality check

  • I ran overcommit --install and all hooks pass
  • I tested my changes thoroughly
    • For provider changes: Re-recorded VCR cassettes with bundle exec rake vcr:record[provider_name]
    • All tests pass: bundle exec rspec
  • I updated documentation if needed
  • I didn't modify auto-generated files manually (models.json, aliases.json)

API changes

  • Breaking change
  • New public methods/classes
  • Changed method signatures
  • No API changes

Related issues

Fixes #497

When using with_schema for structured output and continuing a
conversation, the persisted Hash response was being passed directly
to OpenAI instead of being serialized as JSON string.

OpenAI API expects message content to be a string, so structured
output Hashes stored in content_raw must be converted to JSON when
replayed.

Fixes crmne#497
Copy link

@nerlichman nerlichman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

We faced the same issue on a project and applied locally the changes suggested in this PR, and this fixed our issue.

Comment on lines 12 to 15
return content.value.is_a?(Hash) ? content.value.to_json : content.value
end
return content.to_json if content.is_a?(Hash) || content.is_a?(Array)
return content unless content.is_a?(Content)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the same logic is done on Content::Raw for Hash/Array but using value instead of directly using content.

Maybe something like this could work too:

value = content.is_a?(RubyLLM::Content::Raw) ? content.value : content

return value.to_json if value.is_a?(Hash) || value.is_a?(Array)

return content unless content.is_a?(Content)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I've added an array check for consistency

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Schema breaks multi-turn conversations after persistence

3 participants